/*
"JSFXGEN" - Module.as
Modular Integrated Development Environment for JesuSonic
Project page: http://jsfxgen.googlecode.com

Copyright 2009 Lubomir I. Ivanov

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package neolit123.jsfxgen
{
  // =========================================================  
  import flash.display.Sprite;
  import flash.display.Graphics;
  import flash.display.DisplayObject;
  import flash.geom.Rectangle;
  import flash.text.TextField;   
  import flash.text.TextFieldAutoSize;
  import flash.text.TextFormat;
  import flash.events.MouseEvent;
  import neolit123.ui.Box;
  import neolit123.ui.TextButton;
  // =========================================================
  public class Module extends Sprite
  {
    // =========================================================
    // @ public objects
    public var ip:Array = [];
    public var op:Array = [];
    public var sub:Sprite;
    public var subi:Sprite;
    public var subo:Sprite;
    public var box:Box = new Box(30,suboffset,Main.thmcolor,Main.modbordercolor,true);
    public var btnm:TextButton = new TextButton("--",0xffffff,0x000000,0xffffff);
    public var btne:TextButton = new TextButton("E",0xffffff,0x000000,0xffffff);
    // @ module x,y in modular view
    public var modx:Number = 0;
    public var mody:Number = 0;
    // @ 'amod' dimensions 
    public var modw:Number = 0;
    public var modh:Number = 0;
    // @ id, data(code) and label
    public var modid:int;
    public var moddata:String;
    public var lb:String;
    // @ prvt objects and properties
    private var cs:Sprite;
    private var btnx:TextButton = new TextButton("X",0xffffff,0x000000,0xffffff);
    private var pin:Pin;
    private var txt:TextField = new TextField;
    private var tf:TextFormat = new TextFormat("_sans",10,Main.modtextcolor,true,null,null,null,null,null,3,1);
    private var cmain:Sprite;
    private var lastx:Number;
    private var mheight:Number;
    private var df:Function = Main.debug.df;
    // @ const
    private const cmplength:int = 25;
    private const suboffset:int = 17;
    private const charstring:String = "\\#|\\||\\/|\\:|\\@|\\'|\\|\\{|\\(|\\}|\\)|\\+|\\-|\\<|\\>|\\?|\\=|\\;|\\*|\\^|\\%|\\!|\\&|\\.";
    private const ptrn0:RegExp = new RegExp("("+charstring+"){1}","gi");
    private const ptrn1:RegExp = new RegExp("(0|1|2|3|4|5|6|7|8|9){1}","gi");
    // =========================================================
    // [ create module
    // =========================================================
    public function Module(mname:String,mdata:String,id:int):void
    {
      // @ properties
      modid = id;
      lb = mname;
      name = "m"+modid;
      Main.m_ar[id] = this;
      // @ add cmain
      cmain = new Sprite();
      addChild(cmain);
      // @ add container sprite
      cs = new Sprite();
      cmain.addChild(cs);
      cs.addChild(box);
      cs.buttonMode = true;
      cs.mouseChildren = false;
      // @ add txt
      cs.addChild(txt);
      with (txt)
      {
        width = 1;
        height = suboffset;
        selectable = false;
        defaultTextFormat = tf;
      }
      // @ add sub
      sub = new Sprite();
      cmain.addChild(sub);
      sub.y = suboffset;
      subi = new Sprite();
      sub.addChild(subi);
      subo = new Sprite();
      sub.addChild(subo);
      // @ check if new or loaded
      if (mname == "new")
      {
        newmod();
      } else {
        moddata = mdata;
        lb = mname;        
      }
      // @ box drag      
      cs.addEventListener(MouseEvent.MOUSE_OVER, mo);
      function mo(event:MouseEvent):void
      {
        var mc:Object = event.target.parent.parent.parent;
        mc.setChildIndex(event.target.parent.parent, mc.numChildren-1);
      }
      cs.addEventListener(MouseEvent.MOUSE_DOWN, md, false, 0, true);
      function md(event:MouseEvent):void
      {
        Main.movemodule = true;
        var rx:Number,ry:Number,rw:Number,rh:Number;
        rx=15-Main.mcd.x+Main.listwidth;
        ry=25-Main.mcd.y;
        rw=Main.swd-txt.width-50;
        rh=Main.sh-height-40;
        startDrag(false,new Rectangle(rx,ry,rw,rh));
        stage.addEventListener(MouseEvent.MOUSE_UP,mu, false, 0, true);
      }
      function mu(event:MouseEvent):void
      {
        Main.movemodule = false;
        stopDrag();
        stage.removeEventListener(MouseEvent.MOUSE_UP, mu);
      }
      // @ add control buttons
      addChild(btnm);
      btnm.addEventListener(MouseEvent.MOUSE_DOWN, togglehide, false, 0, true);
      addChild(btne);
      btne.addEventListener(MouseEvent.MOUSE_DOWN, editor_f, false, 0, true);
      function editor_f(e:MouseEvent=null):void {
        df("EDIT Module [ "+modid+" ]");
        Main.editor.open(modid,lb,moddata, true);
      }
      addChild(btnx);
      btnx.addEventListener(MouseEvent.MOUSE_DOWN, del_f, false, 0, true);
      function del_f(e:MouseEvent=null):void {
        Main.dialog.update("Delete this module?","Yes","Cancel", deletemod, Main.dialog.close);
      }
      // @ add main sub      
      btnm.visible = false;
      // @ update
      update();
    }    
    // =========================================================
    // [ togglehide
    // =========================================================
    private function togglehide(e:MouseEvent = null):void
    {
      if (sub.visible) mhide() else mshow();
    }
    // =========================================================
    // [ hide
    // =========================================================
    public function mhide():void
    {
      if (sub.visible) {
        btnm.txt.text = "^";
        sub.visible = false;
        box.setsize(null,suboffset);
        Main.getmain().redrawconnections();
      }
    }
    // =========================================================
    // [ show
    // =========================================================
    public function mshow():void
    {
      if (!sub.visible) {
        btnm.txt.text = "--";
        sub.visible = true;
        box.setsize(null,mheight);
        Main.getmain().redrawconnections();
      }
    }
    // =========================================================
    // [ update pins
    // =========================================================
    private function updatepins(inpins:Array,outpins:Array,err:Boolean):void
    {
      var i:int;
      var tmpi:int;
      // ---------------------------------------------------------
      // @ input pins update
      // ---------------------------------------------------------
      if (inpins.join("") != ip.join(""))
      {
        if (!err) df("Module [ "+modid+" ] - updating INPUT pins");
        // @ delete connections to input pins
        deleteincon();
        // @ refresh input pins
        sub.removeChild(subi);
        subi = new Sprite();
        sub.addChild(subi);
        ip = [];
        var inlength:int = inpins.length;
        for (i=0;i<inlength;i++)
        {
          // @ create pin and add tooltip
          pin = new Pin(true, 0, i*10, i, modid, "in: "+inpins[i]);
          pin.name = inpins[i];
          subi.addChild(pin);
          ip.push(inpins[i]);
          Main.tt.add(pin,pin.name);
        }
      }
      // ---------------------------------------------------------
      // @ output pins update
      // ---------------------------------------------------------
      if (outpins.join("") != op.join(""))
      {      
        if (!err) df("Module [ "+modid+" ] - updating OUTPUT pins");
        // @ delete connections to output pins
        deleteoutcon();
        // @ refresh output pins
        sub.removeChild(subo);
        subo = new Sprite();
        sub.addChild(subo);
        op = [];
        var outlength:int = outpins.length;
        for (i=0;i<outlength;i++)
        {
          // @ create pin and add tooltip
          pin = new Pin(false, 0, i*10, i, modid, "out: "+outpins[i]);
          pin.name = outpins[i];
          subo.addChild(pin);
          op.push(outpins[i]);
          Main.tt.add(pin,pin.name);
        }
      }
      // @ set placement, sizes, connections      
      if (subi.height > subo.height) tmpi=subi.height else tmpi = subo.height;
      mheight = suboffset+tmpi;
      if (sub.visible) box.setsize(null,mheight);
      if (tmpi>0) btnm.visible = true else btnm.visible = false;
      if (lastx != (subo.x = txt.width-10)) Main.getmain().redrawconnections();
      lastx = subo.x;
    }
    
    // =========================================================
    // [ update label
    // =========================================================
    private function updatelabel():void    
    {
      var mname:String = lb;
      if (mname.length > cmplength)
      {
        mname = mname.substring(0,cmplength-3)+"...";
      }      
      txt.autoSize = "left";
      txt.text = "[ "+modid+" ] "+mname;
      if (txt.width < 45)
      {
        txt.autoSize = "none";
        txt.width = 45;
      }
      box.setsize(txt.width,null);
      btnm.y = btne.y = btnx.y = -15;
      btnm.x = txt.width-3*15;
      btne.x = btnm.x+15;
      btnx.x = btne.x+15;
    }
    // =========================================================
    // [ public update
    // =========================================================
    public function update():void
    {
      updatelabel();    
      var genar:Array = Main.parser.parsemod(modid,moddata);
      if (genar[0] == "error") {
        updatepins([],[],true);
      } else {
        updatepins(genar[0],genar[1],false)
      }
    }
    // =========================================================
    // [ delete module
    // =========================================================
    private function deletemod():void
    {
      df("DELETE [ "+modid+" ]");
      Main.mc.removeChild(Main.m_ar[modid]);
      Main.m_ar[modid] = null;
      Main.m_ar[modid] = undefined;
      // @ delete all connections to module
      deleteincon();
      deleteoutcon();
      Main.dialog.close();
    }
    // =========================================================
    // [ delete connections to input pins
    // =========================================================
    private function deleteincon():void
    {
      var i:int;
      var conlen:int;
      var j:int;
      var spr:Pin;
      for (i=0;i<subi.numChildren;i++)
      {
        spr = subi.getChildAt(i) as Pin;
        conlen = Main.connections.length;
        for (j=0;j<conlen;j++)
        {
          if (Main.connections[j] != undefined)
          {
            if (spr == Main.connections[j].src
            || spr == Main.connections[j].dest)
            {
              Main.connections[j].remove();
            }
          }
        }
      }
    }
    // =========================================================
    // [ delete connections to output pins
    // =========================================================
    private function deleteoutcon():void
    {
      var i:int;
      var conlen:int;
      var j:int;
      var spr:Pin;
      for (i=0;i<subo.numChildren;i++)
      {
        spr = subo.getChildAt(i) as Pin;
        conlen = Main.connections.length;
        for (j=0;j<conlen;j++)
        {
          if (Main.connections[j] != undefined)
          {
            if (spr == Main.connections[j].src
            || spr == Main.connections[j].dest)
            {
              Main.connections[j].remove();
            }
          }
        }
      }
    }
    // =========================================================
    // [ new module
    // =========================================================
    private function newmod():void
    {
      lb = "new module";
      moddata = "";
      moddata += "// *************************\n";
      moddata += "// new module\n";
      moddata += "// *************************\n";
      moddata += "//#mspr\n";
      moddata += "//#inputs: in0[sample], in1[sample]\n";
      moddata += "//#outputs: out0[sample], out1[sample]\n";
      moddata += "//#declare:\n";
      moddata += "//#declare:\n\n";
      moddata += "//#mspr\n";
      moddata += "//'slider' and 'filename' code here\n\n";
      moddata += "//#mspr\n";
      moddata += "@init\n\n";
      moddata += "//#mspr\n";
      moddata += "@slider\n\n";
      moddata += "//#mspr\n";
      moddata += "@sample\n\n";
      moddata += "//#mspr\n";
      moddata += "@block\n\n";
      moddata += "//#mspr\n";
      moddata += "@gfx\n\n";
      moddata += "//#mspr\n";
      moddata += "@serialize\n";
    }    
  }
}